package coordinator

import (
	"adai.design/homemaster/log"
	"bufio"
	"errors"
	"github.com/tarm/serial"
	"runtime"
	"sync"
)

type MessageObserverFunc func(m *Message) error

var serialLinkPort = func() string {
	if runtime.GOARCH == "mipsle" {
		return "/dev/ttyS1"
	} else {
		return "/dev/tty.SLAB_USBtoUART"
	}
}()

type serialClient struct {
	sync.Mutex
	observers map[uint16]MessageObserverFunc

	dev  *serial.Port
	task chan []byte
}

func addObserver(msgType uint16, f MessageObserverFunc) {
	client.Lock()
	defer client.Unlock()
	client.observers[msgType] = f
}

func removeObserver(msgType uint16) {
	client.Lock()
	defer client.Unlock()
	delete(client.observers, msgType)
}

var client = serialClient{
	observers: make(map[uint16]MessageObserverFunc),
	task:      make(chan []byte, 50),
}

func (c *serialClient) executeTask() {
	for task := range c.task {
		if c.dev != nil {
			if _, err := c.dev.Write(task); err != nil {
				log.Fatal("err: ", err)
			}
			//log.Printf("task: %x\n", task)
			//time.Sleep(time.Millisecond * 500)
			//time.Sleep(time.Second)
		}
	}
}

func (c *serialClient) readMessage() {
	reader := bufio.NewReader(client.dev)
	for {
		buf, err := reader.ReadSlice(0x03)
		if err != nil {
			log.Println(err)
			break
		}
		log.Printf("read: %x\n", buf)
		msg := newMessage(buf)
		if msg != nil {
			if f, ok := client.observers[msg.MsgT]; ok {
				if err = f(msg); err != nil {
					log.Error("%s", err)
				}
			}
		}
	}
	log.Println("the end")
}

func serialOpen() error {
	cfg := &serial.Config{Name: serialLinkPort, Baud: 115200}
	com, err := serial.OpenPort(cfg)
	if err != nil {
		log.Println(err)
		return err
	}

	client.dev = com
	go client.executeTask()
	go client.readMessage()
	return nil
}

func serialClose() {
	if client.dev != nil {
		client.dev.Close()
	}
	close(client.task)
}

func addTask(data []byte) error {
	if client.task != nil {
		client.task <- data
		return nil
	}
	return errors.New("task channel closed")
}

func sendData(data []byte) ([]byte, error) {
	cfg := &serial.Config{Name: serialLinkPort, Baud: 115200}
	com, err := serial.OpenPort(cfg)
	if err != nil {
		return nil, err
	}
	defer com.Close()
	_, err = com.Write(data)
	if err != nil {
		return nil, err
	}

	buf := make([]byte, 1024)
	n, err := com.Read(buf)
	if err != nil {
		return nil, err
	}
	return buf[:n], nil
}
